#include "VolumeWriter.h"
#include "VolumeGridData.h"
#include "OCMain/zip/gzstream.h"
#include "OCMain/Log.h"
#include "OCMain/FileUtil.h"
#include "VolumeMesh.h"

namespace OCPP
{
	namespace Volume
	{

		VolumeWriter::~VolumeWriter()
		{

		}

		String VolumeWriter::generateOutputName(const VolumeGridData& ncGridData, const VolumeOptions& ncOptions)
		{
			StringStream sstream;
			for (auto it = std::rbegin(ncGridData._dimensions); it != std::rend(ncGridData._dimensions); it++)
			{
				if (0 != sstream.tellp())
				{
					sstream << _dimSeperator;
				}
				String dim = *it;
				dim = StringUtil::replaceAll(dim, "\\", "");
				dim = StringUtil::replaceAll(dim, "/", "");
				sstream << dim;
			}

			if (!_typeExtension.empty())
			{
				sstream << "." << _typeExtension;
			}

			if (VolumeWriter::getGZipCompress())
			{
				sstream << ".gz";
			}

			return sstream.str();
		}

		std::unique_ptr<std::ostream> VolumeWriter::getOutStream(const GridData& ncGridData, const VolumeOptions& ncOptions, bool binary)
		{
			String fileName = "";

			String time = ncGridData.time;
			String varName = ncGridData.varName;
			String level = ncGridData.level;

			if (customOutputName.has_value())
			{
				varName = customOutputName.value();
				level = "";
				time = "";
			}

			if (varName.empty()) {
				varName = time;
				time = "";
			}
			if (varName.empty()) {
				varName = level;
				level = "";
			}

			fileName = joinPath(getOutputPath(),varName);

			if (!level.empty()) {
				fileName = fileName + _dimSeperator + level;
			}
			if (!time.empty()) {
				fileName = fileName + _dimSeperator + time;
			}

			fileName += "." + _typeExtension;

			if (getMakeDirForFile())
			{
				FileUtil::makeDirectory(FileUtil::getFilePath(fileName));
			}

			std::unique_ptr<std::ostream> outStream;

			if (getGZipCompress())
			{
				fileName += ".gz";
				outStream = std::make_unique<ogzstream>(fileName.c_str());
			}
			else
			{
				if (binary) {
					outStream = std::make_unique<std::ofstream>(fileName, std::ios::binary);
				}
				else {
					outStream = std::make_unique<std::ofstream>(fileName);
				}
			}

			if (!(*outStream.get()))
			{
				OCPP_LOG_INFO << fileName << " can not created!\n";
				outStream = nullptr;
			}

			return outStream;
		}

		std::unique_ptr<std::ostream> VolumeWriter::getOutStream(const VolumeGridData& ncGridData, const VolumeOptions& ncOptions)
		{
			const String outputPath = getOutputPath();
			const bool gzipCompress = getGZipCompress();

			String name = generateOutputName(ncGridData, ncOptions);

			String fileName = outputPath + "/" + name;

			if (_mkdirForFile)
			{
				FileUtil::makeDirectory(FileUtil::getFilePath(fileName));
			}

			std::unique_ptr<std::ostream> outStream;

			if (gzipCompress)
			{
				outStream = std::make_unique<ogzstream>(fileName.c_str());
			}
			else
			{
				if (_binary) {
					outStream = std::make_unique<std::ofstream>(fileName, std::ios::binary);
				}
				else {
					outStream = std::make_unique<std::ofstream>(fileName);
				}
			}

			if (!(*outStream.get()))
			{
				OCPP_LOG_INFO << fileName << " can not created!\n";
				outStream = nullptr;
			}

			return outStream;
		}

		bool VolumeWriter::write(GridData& ncGridData, const VolumeOptions& ncOptions)
		{
			return true;
		}

		bool VolumeWriter::write(const VolumeGridData& ncGridData, const VolumeOptions& ncOptions)
		{
			if (_isoValues.size() == 0) return true;
			const bool gzipCompress = getGZipCompress();

			_typeExtension = "iso";
			const String outputPath = getOutputPath();
			
			VolumeGridData isoGridData;
			isoGridData.copyFrom(ncGridData);

			if (ncGridData._vecFloat.size() > 0)
			{
				isoGridData._vecFloat = std::move(const_cast<FloatVector&>(ncGridData._vecFloat));
			}
			else if (ncGridData._vecChar.size() > 0)
			{
				int dimZ = ncGridData.getExtent().dimZ;
				int dimY = ncGridData.getExtent().dimY;
				int dimX = ncGridData.getExtent().dimX;
				int dimXYZ = dimX * dimY * dimZ;
				int comp = ncGridData._vecChar.size() / dimXYZ;
				isoGridData._vecFloat.resize(dimXYZ);
				for (int z = 0; z < dimZ; z++)
				{
					for (int y = 0; y < dimY; y++)
					{
						for (int x = 0; x < dimX; x++)
						{
							int i = z * dimY * dimX + y * dimX + x;
							const uint8* rgba = &ncGridData._vecChar[i * 4];
							if (comp == 4)
							{
								float u = rgba[0] - 127.0 + rgba[1] / 255.0;
								float v = rgba[2] - 127.0 + rgba[3] / 255.0;
								isoGridData._vecFloat[i] = Vector2(u, v).length();
							}
						}
					}
				}
			}
			
			if (isoGridData._vecFloat.size() == 0)
			{
				return false;
			}

			for (const auto& isoValue : _isoValues)
			{
				_typeExtension = str_format("%.1f.iso",isoValue);
				String name = generateOutputName(ncGridData, ncOptions);
				String fileName = joinPath(outputPath, name);
				VolumeMesh mesh;
				auto fv = mesh.run(isoValue, isoGridData, ncOptions);
				if (fv.size() == 0)
				{
					continue;
				}

				std::unique_ptr<std::ostream> outStream;
				if (gzipCompress)
				{
					outStream = std::make_unique<ogzstream>(fileName.c_str());
				}
				else
				{
					outStream = std::make_unique<std::ofstream>(fileName, std::ios::binary);
				}
				outStream->write((char*)&fv[0], fv.size() * sizeof(float));
			}

			return true;
		}
	}
}


